﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MethodWorx.Core.DomainModel;
using System.Data.Linq;
using MethodWorx.Core.Linq;

namespace MethodWorx.CMS.Linq
{
    public abstract class LinqGenericStorageRepository<T> : IRepository<T>
        where T : AggregateRoot
    {
        private DataContext context;

        public LinqGenericStorageRepository(DataContext context)
        {
            this.context = context;
        }

        protected abstract string Type { get; }

        #region IRepository<T> Members

        public ITable<LinqGenericStorage> Storage
        {
            get { return this.context.GetTable<LinqGenericStorage>(); }
        }

        public ITable<LinqGenericStorageIndex> Indexes
        {
            get { return this.context.GetTable<LinqGenericStorageIndex>(); }
        }

        public IEnumerable<T> All()
        {
            return new GenericStorageEnumerable<T>(this.Storage.Where(s => s.Type == this.Type));
        }

        public IQueryable<T> Query()
        {
            throw new NotImplementedException();
        }

        public T this[Guid id]
        {
            get
            {
                LinqGenericStorage storage = this.Storage.FirstOrDefault(s => s.Id == id);
                if (storage == null)
                    return null;
                else
                    return (T)storage.ObjectStorage.Base64DeserializeUsingXml();
            }
        }

        public T CreateEntityInstance()
        {
            T entity =  Activator.CreateInstance<T>();
            entity.Id = Guid.NewGuid();

            return entity;
        }

        public virtual object CreateInstance(Type type)
        {
            return Activator.CreateInstance(type);
        }

        public bool Contains(T entity)
        {
            return (this[entity.Id] != null);
        }

        protected virtual IEnumerable<LinqGenericStorageIndex> GetIndexes(T entity)
        {
            return new LinqGenericStorageIndex[] { };
        }

        public void Insert(T entity)
        {
            //  insert
            LinqGenericStorage storage = new LinqGenericStorage();

            storage.Id = entity.Id;
            storage.ObjectStorage = entity.Base64SerializeUsingXml();
            storage.Type = this.Type;

            this.Storage.InsertOnSubmit(storage);

            foreach(LinqGenericStorageIndex index in this.GetIndexes(entity))
            {
                this.Indexes.InsertOnSubmit(index);
            }
        }

        public void Delete(T entity)
        {
            //  delete
            //  update
            LinqGenericStorage storage = this.Storage.FirstOrDefault(s => s.Id == entity.Id);
            if (storage != null)
            {
                this.Storage.DeleteOnSubmit(storage);

                //  and update the indexes
                foreach (LinqGenericStorageIndex index in this.Indexes.Where(i => i.GenericStorageId == entity.Id))
                    this.Indexes.DeleteOnSubmit(index);
            }
        }

        public void Update(T entity)
        {
            //  update
            LinqGenericStorage storage = this.Storage.FirstOrDefault(s => s.Id == entity.Id);
            if (storage != null)
            {
                storage.ObjectStorage = entity.Base64SerializeUsingXml();
                storage.Type = this.Type;

                //  and update the indexes
                foreach (LinqGenericStorageIndex index in this.Indexes.Where(i => i.GenericStorageId == entity.Id))
                    this.Indexes.DeleteOnSubmit(index);

                 foreach(LinqGenericStorageIndex index in this.GetIndexes(entity))
                {
                    this.Indexes.InsertOnSubmit(index);
                }
            }
            else
                this.Insert(entity);
        }

        #endregion

        #region IRepository Members

        public void Insert(object entity)
        {
            //  insert
            this.Insert((T)entity);
        }

        public void Delete(object entity)
        {
            //  delete
            this.Delete((T)entity);
        }

        public void Update(object entity)
        {
            //  update
            this.Update((T)entity);
        }

        #endregion
    }
}
